//fl2440_spi.c
/***************************************************/
#include <linux/irq.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#include <linux/spinlock.h>
#include <asm/system.h>
#include <asm/uaccess.h>
 
int loopChar=0x88;
module_param(loopChar,int,S_IRUGO);
                                                     
static int spi_major = 55;
#define spi_name "mini2440_spi"
struct cdev spiCdev;
 
/*****************************************************/
 
static int spi_open(struct inode *,struct file *);
static int spi_release(struct inode *,struct file *);
static ssize_t spi_write(struct file *filp,const char *buf,size_t count,loff_t *f_ops);
static ssize_t spi_read(struct file *filp,char *buf,size_t count,loff_t *f_ops);
static ssize_t spi_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long data);
 
volatile int *spi_gpfcon=NULL;//GPF Part define
volatile int *spi_gpfdat=NULL;
volatile int *spi_gpfup=NULL;
 
volatile int *spi_gpgcon=NULL;//GPG Part define
volatile int *spi_gpgdat=NULL;
volatile int *spi_gpgup=NULL;
 
volatile int *s3c2440_clkcon;
volatile int *spi_spcon1;//SPI Part define
volatile int *spi_spsta1;
volatile int *spi_sppin1;
volatile int *spi_sppre1;
volatile int *spi_sptdat1;
volatile int *spi_sprdat1;
 
#define SPI_TXRX_READY      (((*spi_spsta1)&0x1) == 0x1)
/**********************************************************/
static const struct file_operations spi_fops = 
{
    .owner=THIS_MODULE,
    .open=spi_open,
    .read=spi_read,
    .ioctl=spi_ioctl,
    .release=spi_release,
    .write=spi_write,
};
 
/********************************************************/
static int spi_open(struct inode *inode,struct file *filp)
{
    filp->private_data =&spiCdev;    
    /***********************************************
    *PCLK
    ************************************************/
    /*control PCLK into spi block*/
    *s3c2440_clkcon |=0x40000;
    printk("s3c2440_clkcon=%08X\n",*s3c2440_clkcon);
    /***********************************************
    *GPG PORTS
    ************************************************/
    /*config SCK1,MOSI1,MISO1 = 11*/
    *spi_gpgcon |=0x0000FC00;
    /*poll up MISO1 MOSI1,SCK1*/
    *spi_gpgup &=0xFF1F;
    *spi_gpgup |=0x0060;
    /***********************************************
    *GPF PORTS
    ************************************************/
    *spi_gpfcon &= 0xFCF3;
    *spi_gpfcon |= 0x0108;  
    *spi_gpfup &= 0xED;
    *spi_gpfdat |= 0x10;
    /***********************************************
    *SPI REGS
    ************************************************/
    //SPI Baud Rate Prescaler Register,Baud Rate=PCLK/2/(Prescaler value+1)
    *spi_sppre1=0x18;       //freq = 1M
    printk("spi_sppre1=%02X\n",*spi_sppre1);
    //polling,en-sck,master,low,format A,nomal = 0 | TAGD = 1
    *spi_spcon1=(0<<6)|(0<<5)|(1<<4)|(1<<3)|(0<<2)|(0<<1)|(0<<0);
    printk("spi_spcon1=%02X\n",*spi_spcon1);
    //Multi Master error detect disable,reserved,rech=*spi_sprdat0;lease
    *spi_sppin1=(0<<2)|(0<<0);
    printk("spi_sppin1=%02X\n",*spi_sppin1);
 
    return 0;
}
 
 
static int spi_release(struct inode *inode,struct file *filp)
{
    //free irq
    free_irq(IRQ_EINT1, NULL);
 
    printk("<1>release\n");
    return 0;
}
 
 
static void writeByte(const char c)
{
    int j = 0;
    *spi_sptdat1 = c;
    for(j=0;j<0xFF;j++);
    while(!SPI_TXRX_READY)
        for(j=0;j<0xFF;j++);
}
 
static char readByte(void)
{
    int j = 0;
    char ch = 0;
    *spi_sptdat1 = (char)loopChar;
    for(j=0;j<0xFF;j++);
    while(!SPI_TXRX_READY)
        for(j=0;j<0xFF;j++);
    ch=*spi_sprdat1;
    return ch;
}
static ssize_t spi_read(struct file *filp,char __user *buf,size_t count,loff_t *f_ops)
{
    //int len=0;
    char ch;
    printk("<1>spi read!\n");
    ch=readByte();
    copy_to_user(buf,&ch,1);
    return 1;
}
 
static ssize_t spi_write(struct file *filp,const char __user *buf,size_t count,loff_t *f_ops)
{
    int i;
    char *kbuf;
    printk("<1>spi write!,count=%d\n",count);
    kbuf=kmalloc(count,GFP_KERNEL);
    if(copy_from_user(kbuf,buf,count))
    {
        printk("no enough memory!\n");
        return -1;
    }
     
    for(i=0;i<count;i++)
    {
        writeByte(*kbuf);
        printk("write 0x%02X!\n",*kbuf);
        kbuf++;
    }
    return count;
}
 
static ssize_t spi_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long data)
{
    return 0;
}
 
 
static int __init spi_init(void)
{
    int result;
    dev_t devno = MKDEV(spi_major, 0);
 
    /**/
    if (spi_major)
        result = register_chrdev_region(devno, 1, spi_name);
    else  /**/
    {
        result = alloc_chrdev_region(&devno, 0, 1, spi_name);
        spi_major = MAJOR(devno);
    }  
    if (result < 0)
        return result;
         
    cdev_init(&spiCdev, &spi_fops);
    spiCdev.owner = THIS_MODULE;
    spiCdev.ops = &spi_fops;
    if (cdev_add(&spiCdev, devno, 1))
        printk(KERN_NOTICE "Error adding spi %d", 0);
 
    s3c2440_clkcon = (int *)ioremap(0x4C00000c,3);
 
    spi_gpgcon = (int *)ioremap (0x56000060,4);
    spi_gpgdat = (int *)ioremap (0x56000064,2);
    spi_gpgup = (int *)ioremap (0x56000068,2);
 
    spi_gpfcon = (int *)ioremap (0x56000050,2);
    spi_gpfdat = (int *)ioremap (0x56000054,1);
    spi_gpfup = (int *)ioremap (0x56000058,1);
 
    spi_spcon1 = (int *)ioremap(0x59000020,1);
    spi_spsta1 = (int *)ioremap(0x59000024,1);
    spi_sppin1 = (int *)ioremap(0x59000028,1);
    spi_sppre1 = (int *)ioremap(0x5900002c,1);
    spi_sptdat1 = (int *)ioremap(0x59000030,1);
    spi_sprdat1 = (int *)ioremap(0x59000034,1);
 
 
    printk("Init spi success!\n");  
    return result;
}
 
 
static void __exit spi_exit(void)
{
    cdev_del(&spiCdev);
    unregister_chrdev_region(MKDEV(spi_major, 0), 1);
    printk("<1>spi_exit!\n");
}
 
module_init(spi_init);
module_exit(spi_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("nkzc");
MODULE_DESCRIPTION("SPI driver for S3C2440");